package filter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.WriteListener;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;


/*
@WebFilter
(
	description = "GZIP压缩过滤器",
	filterName = "GZIPFilter",
	urlPatterns = {"/*"},
	asyncSupported = true
)
*/


public class GZIPFilter implements Filter
{
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException
	{
		HttpServletRequest http_request = (HttpServletRequest)request;
		
		String accept_encoding = http_request.getHeader("Accept-Encoding");
		if (accept_encoding != null && !accept_encoding.equals(""))
		{
			if (accept_encoding.toLowerCase().indexOf("gzip") != -1)
			{
				GZIPResponseWrapper wrapper_response = new GZIPResponseWrapper((HttpServletResponse)response);
				chain.doFilter(http_request, wrapper_response);
				wrapper_response.finishResponse();
				return;
			}
		}
		
		chain.doFilter(request, response);
		
		
		
	}

}


/**
 * GZIP Response包装类
 * */
class GZIPResponseWrapper extends HttpServletResponseWrapper
{
	private HttpServletResponse response = null;
	private ServletOutputStream output_stream = null;
	private PrintWriter writer = null;

	public GZIPResponseWrapper(HttpServletResponse response)
	{
		super(response);
		this.response = response;
	}
	
	
	@Override
    public ServletOutputStream getOutputStream()
    		throws IOException
	{
		if (writer != null)
        	throw new IllegalStateException("getWriter() has all ready been called!");
		
		if (this.output_stream == null)
			this.output_stream = new GZIPResponseOutputStream(this.response);
		
		return this.output_stream;
    }
	
	
	@Override
    public PrintWriter getWriter()
    		throws IOException
	{
		if (this.writer != null)
			return this.writer;
		
        if (output_stream != null)
        	throw new IllegalStateException("getOutputStream() has all ready been called!");
        
        
        this.output_stream = new GZIPResponseOutputStream(this.response);
        this.writer = new PrintWriter(new OutputStreamWriter(this.output_stream, "UTF-8"));
		return this.writer;
    }
	
	public void finishResponse()
	{
		if (this.writer != null)
			this.writer.close();
		
		try
		{
			if (this.output_stream != null)
				this.output_stream.close();
		}
		catch (IOException e) {e.printStackTrace();}
	}
}



/**
 * GZIP Response输出流
 * */
class GZIPResponseOutputStream extends ServletOutputStream
{
	HttpServletResponse response = null;
	GZIPOutputStream gzip_output_stream = null;
	ServletOutputStream output_stream = null;
	ByteArrayOutputStream stream = null;
	
	public GZIPResponseOutputStream(HttpServletResponse response)
	{
		super();
		this.response = response;
		
		
		try
		{
			this.output_stream = this.response.getOutputStream();
			stream = new ByteArrayOutputStream();
			this.gzip_output_stream = new GZIPOutputStream(stream);
		}
		catch (IOException e) {e.printStackTrace();}
	}
	
	@Override
	public boolean isReady()
	{
		
		return false;
	}

	@Override
	public void setWriteListener(WriteListener listener)
	{
		
	}

	@Override
	public void write(int b)
			throws IOException
	{
		this.gzip_output_stream.write(b);
	}
	
	
	@Override
	public void write(byte[] b)
			throws IOException
	{
		this.gzip_output_stream.write(b);
	}
	
	
	@Override
    public void write(byte[] b, int off, int len)
    		throws IOException
	{
		this.gzip_output_stream.write(b, off, len);
    }
	
	@Override
	public void flush() throws IOException
	{
		this.gzip_output_stream.flush();
	}
	
	@Override
	public void close() throws IOException
	{
		this.gzip_output_stream.finish();
		
		byte[] bytes = this.stream.toByteArray();
		
		//this.response.addHeader("Content-Length", Integer.toString(bytes.length));
		this.response.addHeader("Content-Encoding", "gzip");
		
		this.output_stream.write(bytes);
		this.output_stream.flush();
		this.output_stream.close();
	}
}


















